package org.sean.framework.web.holder;

import org.apache.commons.lang3.StringUtils;
import org.sean.framework.auth.token.JWT;
import org.sean.framework.code.StatusInfo;
import org.sean.framework.context.SpringApplicationContext;
import org.sean.framework.exception.StatusException;
import org.sean.framework.util.NumberUtil;
import org.sean.framework.util.ObjectUtil;
import org.sean.framework.web.filter.HeaderFilter;
import org.sean.framework.web.filter.LogFilter;
import org.springframework.http.HttpHeaders;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * RequestHolder
 *
 * @author xielei
 */
public class RequestHolder {
    private RequestHolder() {
    }

    /**
     * 执行方法
     *
     * @param fun 处理器
     * @param cls 类型
     * @param <T> T
     * @param <C> C
     */
    private static <T, C> T executeFilterMethod(Function<C, T> fun, Class<C> cls) {
        C instance = SpringApplicationContext.getBean(cls);
        if (instance == null) {
            throw new StatusException(StatusInfo.serverInitError().setAnyMessage("启动异常: " + cls.getName() + " Not Found!"));
        }
        return fun.apply(instance);
    }

    /**
     * 获取JWT
     *
     * @return JWT
     */
    public static JWT getJWT() {
        return executeFilterMethod(HeaderFilter::getJWT, HeaderFilter.class);
    }

    /**
     * 获取JWT
     *
     * @return JWT
     */
    public static String getJWTValue() {
        return executeFilterMethod(HeaderFilter::getHeaderJWT, HeaderFilter.class);
    }


    /**
     * 请求头中的jwt中 取 Uid
     *
     * @return uid
     */
    public static int getUid() {
        try {
            return getUid(false);
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 请求头中的jwt中 取 Uid
     *
     * @return uid
     */
    private static int getUid(boolean verifyJWT) {
        JWT jwt = getJWT();
        if (jwt == null || jwt.getPayload() == null) {
            throw new StatusException(StatusInfo.notLoginError());
        }
        if (verifyJWT) {
            jwt.validate(true);
        }

        return NumberUtil.getInteger(jwt.getPayload().getUid(), 0);
    }

    /**
     * 请求头中的jwt中 取 Uid
     *
     * @return uid
     */
    public static int getVerifiedUid() {
        return getUid(true);
    }

    /**
     * Return the Request Headers currently bound to the thread.
     *
     * @return the Request Headers currently bound to the thread,
     * or {@code null} if none bound
     */
    public static Map<String, String> getHeaders() {
        return executeFilterMethod(HeaderFilter::getHeaders, HeaderFilter.class);
    }

    /**
     * 获取Collection Header
     *
     * @return Headers
     */
    public static Map<String, Collection<String>> getCollectionHeaders() {
        return executeFilterMethod(HeaderFilter::getCollectionHeaders, HeaderFilter.class);
    }

    /**
     * UV
     *
     * @return targetId
     */
    public static String getTargetID() {
        return executeFilterMethod(HeaderFilter::getTargetID, HeaderFilter.class);
    }

    /**
     * @return Source
     */
    public static int getSource() {
        return Optional.ofNullable(executeFilterMethod(HeaderFilter::getSource, HeaderFilter.class))
                .orElse(0);
    }

    /**
     * @return Platform
     */
    public static int getPlatform() {
        return Optional.ofNullable(executeFilterMethod(HeaderFilter::getPlatform, HeaderFilter.class))
                .orElse(0);
    }

    /**
     * @return targetIp
     */
    public static String getTargetIp() {
        return executeFilterMethod(HeaderFilter::getRemoteIp, HeaderFilter.class);
    }

    /**
     * @return Request
     */
    public static HttpServletRequest getRequest() {
        return executeFilterMethod(LogFilter::getRequest, LogFilter.class);
    }

    /**
     * @return 获取客户端ID
     */
    public static String getClientId() {
        return getHeaders().get(HeaderFilter.HEADER_CLIENT_ID);
    }

    /**
     * @return 获取设备ID
     */
    public static String getDeviceId() {
        return getHeaders().get(HeaderFilter.HEADER_DEVICE_ID);
    }

    /**
     * @return 参数
     */
    public static Map<String, String> getParameterMap() {
        HttpServletRequest request = getRequest();
        Enumeration<String> parameters = request.getParameterNames();

        Map<String, String> params = new HashMap<>();
        while (parameters.hasMoreElements()) {
            String parameter = parameters.nextElement();
            String value = request.getParameter(parameter);
            if (StringUtils.isNotBlank(value)) {
                params.put(parameter, value);
            }
        }

        return params;
    }

    /**
     * @return domain
     */

    public static String getDomain() {
        HttpServletRequest request = getRequest();
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    /**
     * @return origin
     */
    public static String getOrigin() {
        return getRequest().getHeader(HttpHeaders.ORIGIN);
    }

    /**
     * @return Language
     */
    public static String getLanguage() {
        //默认语言
        String defaultLanguage = "zh-CN";
        //request
        HttpServletRequest request = getRequest();
        if (request == null) {
            return defaultLanguage;
        }

        //请求语言
        defaultLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);

        return defaultLanguage;
    }

    /**
     * 获取Cookie的值
     *
     * @return Cookie map
     */
    public static Map<String, String> getCookie() {
        Cookie[] cookies = getRequest().getCookies();
        if (ObjectUtil.isEmpty(cookies)) {
            return null;
        }
        return Arrays.stream(cookies).collect(Collectors.toMap(Cookie::getName, Cookie::getValue, (o, l) -> l));
    }
}